#include "Map.h"
#include <stdexcept>
#include <algorithm>

Map::Map(): rowCount(0), columnCount(0), cellmap(0), unitSize(DEFAULT_UNITSIZE)
{
	// Set a default map size
	Resize(DEFAULT_ROWCOUNT, DEFAULT_COLUMNCOUNT);
}

Map::~Map()
{
	Clear();
}

// Release all allocated resources
void Map::Clear()
{
	// Release resources for cells which are still in use
	CleanupCellPointers();

	// Release old cellmap
	if (cellmap)
	{
		delete[] cellmap;

		cellmap = 0;
	}
}

// Copy original cells over to the new map (by copying ONLY the pointer)
void Map::CopyCellPointers(Cell** newcellmap, unsigned int newRowCount, unsigned int newColumnCount)
{
	// Check how many cells we should copy from the original
	unsigned int copyRowCount = std::min(rowCount, newRowCount);
	unsigned int copyColumnCount = std::min(columnCount, newColumnCount);


	// Nothing to copy if no cellmap present
	if (0 == cellmap)
		return;

	for(unsigned int row=0; row<copyRowCount; row++)
	{
		for(unsigned int column=0; column<copyColumnCount; column++)
		{
			unsigned int oldindex = row * columnCount + column;
			unsigned int newindex = row * newColumnCount + column;

			// Copy pointer
			newcellmap[newindex] = cellmap[oldindex];

			// Set original cell pointer to 0, so it won't get deleted (since the copy is using it now)
			cellmap[oldindex] = 0;
		}
	}
}

// Set cell pointers of a cellmap to 0
void Map::ClearCellPointers(Cell** newcellmap, unsigned int newRowCount, unsigned int newColumnCount)
{
	for(unsigned int row=0; row<newRowCount; row++)
	{
		for(unsigned int column=0; column<newColumnCount; column++)
		{
			newcellmap[row * newColumnCount + column] = 0;
		}
	}
}


// Fill empty cells with a new instance of a cell
void Map::InitializeCellPointers(Cell** newcellmap, unsigned int newRowCount, unsigned int newColumnCount)
{
	for(unsigned int row=0; row<newRowCount; row++)
	{
		for(unsigned int column=0; column<newColumnCount; column++)
		{
			unsigned int index = row * newColumnCount + column;

			// Cell uninitialized?
			if (0 == newcellmap[index])
			{
				// Create new empty cell
				newcellmap[index] = new Cell(unitSize);
			}
		}
	}
}

// Release resources for cells which are still in use
void Map::CleanupCellPointers()
{
	// Nothing to clean if no cellmap present
	if (0 == cellmap)
		return;

	// Inspect each cell of the current map
	for(unsigned int row=0; row<rowCount; row++)
	{
		for(unsigned int column=0; column<columnCount; column++)
		{
			unsigned int oldindex = row * columnCount + column;

			// Cell allocated?
			if (cellmap[oldindex])
			{
				// Delete resource
				delete cellmap[oldindex];

				cellmap[oldindex] = 0;
			}
		}
	}
}

void Map::Resize(unsigned int newRowCount, unsigned int newColumnCount)
{
	// Create a new cellmap, which will later become the real cellmap after copying
	Cell** newcellmap = new Cell*[newRowCount * newColumnCount];

	// Clear pointers of new cellmap to 0
	ClearCellPointers(newcellmap, newRowCount, newColumnCount);

	// Copy cells from the original map to the new map
	CopyCellPointers(newcellmap, newRowCount, newColumnCount);

	// Initialize empty cells
	InitializeCellPointers(newcellmap, newRowCount, newColumnCount);

	// Cleanup the old cellmap
	Clear();

	// And use new one
	cellmap = newcellmap;

	rowCount = newRowCount;
	columnCount = newColumnCount;
}

unsigned int Map::GetRowCount() const
{
	return rowCount;
}

unsigned int Map::GetColumnCount() const
{
	return columnCount;
}

Cell* Map::GetCell(unsigned int x, unsigned int y)
{
	if (0 == cellmap || x >= GetColumnCount() || y >= GetRowCount())
	{
		throw std::runtime_error("Invalid cell index");
	}

	return cellmap[y*GetColumnCount()+x];
}

const Cell* Map::GetCell(unsigned int x, unsigned int y) const
{
	if (0 == cellmap || x >= GetColumnCount() || y >= GetRowCount())
	{
		throw std::runtime_error("Invalid cell index");
	}

	return cellmap[y*GetColumnCount()+x];
}


Cell* Map::operator()(unsigned int x, unsigned int y)
{
	return GetCell(x, y);
}

const Cell* Map::operator()(unsigned int x, unsigned int y) const
{
	return GetCell(x, y);
}

const Pixel& Map::GetFloorPixel(unsigned int x, unsigned int y) const
{
	const Cell* cell = GetCell(x / unitSize, y / unitSize);

	return cell->GetFloorPixel(x % unitSize, y % unitSize);
}

const Pixel& Map::GetCeilingPixel(unsigned int x, unsigned int y) const
{
	const Cell* cell = GetCell(x / unitSize, y / unitSize);

	return cell->GetCeilingPixel(x % unitSize, y % unitSize);
}
